package ControlLayer;

import java.util.ArrayList;

import java.util.Date;
import java.util.LinkedList;

import DBLayer.DBRoomBooking;
import DBLayer.IFDBRoomBooking;
import ModelLayer.Guest;
import ModelLayer.KeyWord;
import ModelLayer.Room;
import ModelLayer.RoomBooking;
import ModelLayer.RoomBookingLine;

public class RoomBookingCtr {
	private RBLineCtr rbLineCtr;
	private RBDateCtr rbDateCtr;
	private RoomTypeCtr rtCtr;
	private PersonCtr pCtr;
	private RoomCtr rCtr;
	private RoomBookingLine_RoomCtr rbl_RoomCtr;

	public RoomBookingCtr() {
		rbl_RoomCtr = new RoomBookingLine_RoomCtr();
		rbLineCtr = new RBLineCtr();
		rbDateCtr = new RBDateCtr();
		rtCtr = new RoomTypeCtr();
		pCtr = new PersonCtr();
		rCtr = new RoomCtr();
	}

	public int insertRoomBooking(int rbId, Date createDate, Date startDate,
			Date endDate, double deposit, double rent, String payStatus) {
		RoomBooking rb = new RoomBooking();
		rb.setId(rbId);
		rb.setCreateDate(createDate);
		rb.setStartDate(startDate);
		rb.setEndDate(endDate);
		rb.setDeposit(deposit);
		rb.setRent(rent);
		rb.setPayStatus(KeyWord.valueOf(payStatus));
		IFDBRoomBooking dbRB = new DBRoomBooking();
		return dbRB.insertRoomBooking(rb);
	}

	public void updateDBRoomBooking(RoomBooking oldRB, RoomBooking newRB)
			throws RoomBookingException {
		boolean success = true;
		// delete old roombookinglines
		LinkedList<RoomBookingLine> oldLines = oldRB.getLines();
		int rbId = oldRB.getId();

		rbLineCtr.deleteAllLineForRB(rbId);

		for (RoomBookingLine rbl : oldLines) {
			// rbl_RoomCtr.deleteAllRoomForRBL(rbl.getId());
			// increment number of available room in booking year
			rbDateCtr
					.incrementNrOfAvaiRoom(rbl.getRoomType().getId(),
							oldRB.getStartDate(), oldRB.getEndDate(),
							rbl.getQuantity());
		}

		// insert new lines
		try {
			rbLineCtr.insertAllLineForRB(newRB);
		} catch (RoomBookingException e) {
			RoomBooking rb = getRoomBooking(rbId);
			LinkedList<RoomBookingLine> newLines = rb.getLines();

			// delete newly inserted lines
			rbLineCtr.deleteAllLineForRB(newRB.getId());

			for (RoomBookingLine rbl : newLines) {
				rbDateCtr.incrementNrOfAvaiRoom(rbl.getRoomType().getId(),
						rb.getStartDate(), rb.getEndDate(), rbl.getQuantity());
			}

			for (RoomBookingLine rbl : oldLines) {
				rbLineCtr.insertLine(rbId, rbl.getRoomType().getId(),
						rbl.getQuantity(), rbl.getTotalPrice());

				// decrease available room number in dates for specific room
				// type
				try {
					rbDateCtr.decrementNrOfAvaiRoom(rbl.getRoomType().getId(),
							oldRB.getStartDate(), oldRB.getEndDate(),
							rbl.getQuantity());
				} catch (RoomBookingException e1) {
				}
			}
			throw new RoomBookingException(
					"room booking line update is failed because there is not enough rooms");
		}

		IFDBRoomBooking dbRB = new DBRoomBooking();
		if (dbRB.updateRoomBooking(newRB) == -1) {
			throw new RoomBookingException("RoomBooking update is failed");
		}
	}

	public int updateDBRoomBookingInfo(int rbId, Date createDate,
			Date startDate, Date endDate, double deposit, double rent,
			String payStatus) {
		RoomBooking rb = getRoomBooking(rbId);
		rb.setId(rbId);
		rb.setCreateDate(createDate);
		rb.setStartDate(startDate);
		rb.setEndDate(endDate);
		rb.setDeposit(deposit);
		rb.setRent(rent);
		rb.setPayStatus(KeyWord.valueOf(payStatus));
		IFDBRoomBooking dbRB = new DBRoomBooking();
		return dbRB.updateRoomBooking(rb);
	}

	public int deleteRoomBooking(int rbId) {
		RoomBooking rb = getRoomBooking(rbId);
		LinkedList<RoomBookingLine> lines = rb.getLines();

		for (RoomBookingLine rbl : lines) {
			rbl_RoomCtr.deleteAllRoomForRBL(rbl.getId());
			// increment number of available room in booking year
			rbDateCtr.incrementNrOfAvaiRoom(rbl.getRoomType().getId(),
					rb.getStartDate(), rb.getEndDate(), rbl.getQuantity());
		}
		// delete line
		rbLineCtr.deleteAllLineForRB(rbId);

		IFDBRoomBooking dbRB = new DBRoomBooking();
		return dbRB.deleteRoomBooking(rbId);
	}

	public RoomBooking getRoomBooking(int rbId) {
		IFDBRoomBooking dbrb = new DBRoomBooking();
		return dbrb.getRoomBooking(rbId, true);
	}

	public boolean isRoomBookingAvailable(Date startDate, Date endDate,
			RoomBooking roomBooking) throws RoomBookingException {
		boolean success = true;
		// check if dates are available
		if (roomBooking.getLines() != null) {
			for (RoomBookingLine rbl : roomBooking.getLines()) {
				if (!rbDateCtr.isRoomsAvailableForRT(rbl.getRoomType().getId(),
						rbl.getQuantity(), startDate, endDate)) {
					success = false;
					return success;
				}
			}
		} else {
			throw new RoomBookingException(
					"Please add room booking line to the table");
		}
		return success;
	}

	// TODO create room booking with booking lines in database, update available
	// room numbers for room types
	public boolean createRoomBooking(int rbId, Date createDate, Date startDate,
			Date endDate, double deposit, double rent, String payStatus,
			RoomBooking roomBooking) throws RoomBookingException {
		boolean success = true;
		// insert room booking
		insertRoomBooking(rbId, createDate, startDate, endDate, deposit, rent,
				payStatus);
		// insert room booking line
		for (RoomBookingLine rbl : roomBooking.getLines()) {
			if (rbLineCtr.insertLine(rbId, rbl.getRoomType().getId(),
					rbl.getQuantity(), rbl.getTotalPrice()) == -1) {
				success = false;
				return success;
			}

			// decrease available room number in dates for specific room type
			rbDateCtr.decrementNrOfAvaiRoom(rbl.getRoomType().getId(),
					startDate, endDate, rbl.getQuantity());
		}
		return success;
	}

	// delete room booking with booking lines in database, update available room
	// numbers for room types
	public void cancelRoomBooking(int rbId) {
		RoomBooking roomBooking = getRoomBooking(rbId);
		for (RoomBookingLine rbl : roomBooking.getLines()) {
			rbLineCtr.deleteAllLineForRB(rbId);
			// increase available room number in dates for specific room type
			rbDateCtr.incrementNrOfAvaiRoom(rbl.getRoomType().getId(),
					roomBooking.getStartDate(), roomBooking.getEndDate(),
					rbl.getQuantity());
		}
		deleteRoomBooking(rbId);
	}

	public boolean updateRoomBooking(int rbId, RoomBooking roomBooking)
			throws RoomBookingException {
		boolean success = true;
		// get room booking
		RoomBooking rb = getRoomBooking(rbId);
		// delete room booking before update
		deleteRoomBooking(rb.getId());
		if (!createRoomBooking(rbId, roomBooking.getCreateDate(),
				roomBooking.getStartDate(), roomBooking.getEndDate(),
				roomBooking.getDeposit(), roomBooking.getRent(), roomBooking
						.getPayStatus().toString(), roomBooking)) {
			// if insert new booking line fail, roll back to old booking line
			createRoomBooking(rb.getId(), rb.getCreateDate(),
					rb.getStartDate(), rb.getEndDate(), rb.getDeposit(),
					rb.getRent(), rb.getPayStatus().toString(), rb);
			success = false;
			return success;
		}
		//
		// rbLineCtr.deleteAllLineForRB(rbId);
		// for (RoomBookingLine rbl: rb.getLines()) {
		// rbLineCtr.deleteAllLineForRB(rbId);
		// // increase available room number in dates for specific room type
		// rbDateCtr.incrementNrOfAvaiRoom(rbl.getRoomType().getId(),
		// roomBooking.getStartDate(), roomBooking.getEndDate());
		// }
		// updateDBRoomBooking(roomBooking);
		// for (RoomBookingLine rbl: roomBooking.getLines()) {
		// rbLineCtr.insertLine(rbId, rbl.getRoomType().getId(),
		// rbl.getQuantity(), rbl.getQuantity());
		// // decrease available room number in dates for specific room type
		// rbDateCtr.decrementNrOfAvaiRoom(rbl.getRoomType().getId(),
		// roomBooking.getStartDate(), roomBooking.getEndDate());
		// }
		return success;
	}

	public int checkIn(ArrayList<Integer> guests, int rtId, Date endDate) {
		// find the available room
		Room room = rtCtr.getFirstAvailableRoom(rtId);
		room.setEndDate(endDate);
		room.setStatus(KeyWord.Occupied);
		for (Integer id : guests)
			pCtr.updateGuestRoom(id, room);
		rCtr.updateRoom(room.getId(), room.getName(), room.getEndDate(), room
				.getRoomType().getId(), room.getStatus().toString());
		return room.getId();
	}

	public void checkOut(int roomId) {
		ArrayList<Guest> guests = pCtr.getGuestForRoom(roomId);
		// change room status
		Room room = rCtr.getRoom(roomId);
		room.setStatus(KeyWord.Available);
		room.setEndDate(null);
		rCtr.updateRoom(room.getId(), room.getName(), room.getEndDate(), room
				.getRoomType().getId(), room.getStatus().toString());
		// set guest current room to null
		for (Guest guest : guests) {
			pCtr.updateGuestRoom(guest.getID(), null);
		}
	}

}
